package cn.tedu.mall.product.redis.repo.impl;

import cn.tedu.mall.pojo.product.vo.BrandStandardVO;
import cn.tedu.mall.product.redis.repo.IBrandRedisRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static cn.tedu.mall.product.redis.constant.RedisConst.EMPTY_VALUE;
import static cn.tedu.mall.product.redis.constant.RedisConst.EMPTY_VALUE_TIMEOUT_IN_MINUTE;
import static cn.tedu.mall.product.redis.constant.RedisConst.Key.Brand.*;

@Repository
@Slf4j
public class BrandRedisRepositoryImpl implements IBrandRedisRepository {

    @Autowired
    private RedisTemplate<String, Serializable> redisTemplate;

    @Override
    public void setValue(BrandStandardVO brand) {
        // 根据此数据的id，获取此数据对应的key
        String key = getItemKey(brand.getId());
        // 向Redis中存入数据
        log.debug("写入品牌缓存 >>> key={}, value={}", key, brand);
        redisTemplate.opsForValue().set(key, brand);
    }

    @Override
    public void setEmptyValue(Long id) {
        // 获取此id对应的key
        String key = getItemKey(id);
        // 向Redis中存入空数据，此空数据将在默认到期时间后被自动清除
        log.debug("写入品牌缓存 >>> key={}, value={}, timeout={}min", key, EMPTY_VALUE, EMPTY_VALUE_TIMEOUT_IN_MINUTE);
        redisTemplate.opsForValue().set(key, EMPTY_VALUE, EMPTY_VALUE_TIMEOUT_IN_MINUTE, TimeUnit.MINUTES);
    }

    @Override
    public void setRecentlyLoadTime() {
        long currentTime = System.currentTimeMillis();
        redisTemplate.opsForValue().set(getRecentlyLoadTimeKey(), currentTime);
        log.debug("写入重建品牌数据缓存的时间 >>> {}", currentTime);
    }

    @Override
    public void appendList(BrandStandardVO brand) {
        // 获取列表数据的key
        String listKey = getListKey();
        // 将数据添加到Redis中的原列表的末尾
        log.debug("追加品牌列表 >>> key={}, value={}", listKey, brand);
        redisTemplate.opsForList().rightPush(listKey, brand);
    }

    @Override
    public void appendList(List<BrandStandardVO> brandList) {
        // 获取列表数据的key
        String listKey = getListKey();
        // 遍历参数数据，反复将各元素数据添加到Redis中的原列表的末尾
        for (BrandStandardVO brand : brandList) {
            log.debug("追加品牌列表 >>> key={}, value={}", listKey, brand);
            redisTemplate.opsForList().rightPush(listKey, brand);
        }
    }

    @Override
    public Boolean deleteValue(Long id) {
        // 获取尝试删除的数据的key
        String key = getItemKey(id);
        // 执行删除
        log.debug("删除品牌项 >>> key={}", key);
        return redisTemplate.delete(key);
    }

    @Override
    public Boolean deleteList() {
        // 获取列表数据的key
        String listKey = getListKey();
        // 执行删除
        log.debug("删除品牌列表 >>> key={}", listKey);
        return redisTemplate.delete(listKey);
    }

    @Override
    public void flushAll() {
        // 日志
        log.debug("即将清空品牌缓存……");
        // 获取所有单项数据的key的模式
        String keyPattern = getAllKeyPattern();
        log.debug("品牌缓存的Key模式 >>> {}", keyPattern);
        // 在Redis中检索所有能够匹配模式的key
        Set<String> keys = redisTemplate.keys(keyPattern);
        log.debug("品牌缓存的Key清单 >>> {}", keys);
        // 遍历匹配的key集合
        if (keys != null) {
            for (String key : keys) {
                // 根据key删除Redis中的数据
                log.debug("清空品牌缓存，删除项 >>> key={}", key);
                redisTemplate.delete(key);
            }
        }
    }

    @Override
    public Long getRecentlyLoadTime() {
        Serializable result = redisTemplate.opsForValue().get(getRecentlyLoadTimeKey());
        log.debug("获取最近重建品牌数据缓存的时间 >>> {}", result);
        return (Long) result;
    }

    @Override
    public Serializable getValue(Long id) {
        // 获取尝试访问的数据的key
        String key = getItemKey(id);
        // 返回检索结果
        Serializable result = redisTemplate.opsForValue().get(key);
        log.debug("获取品牌项 >>> key={}, result={}", key, result);
        return result;
    }

    @Override
    public Long listSize() {
        return redisTemplate.opsForList().size(getListKey());
    }

    @Override
    public List<BrandStandardVO> list() {
        // 获取列表数据的key
        String listKey = getListKey();
        // 将检索的列表数据的起始索引，0表示从头开始获取数据
        long start = 0;
        // 将检索的列表数据的结束索引，-1表示最末尾
        long end = -1;
        // 返回检索结果
        return getList(listKey, start, end);
    }

    @Override
    public List<BrandStandardVO> list(Integer page, Integer pageSize) {
        // 获取列表数据的key
        String listKey = getListKey();
        // 将检索的列表数据的起始索引，0表示从头开始获取数据
        long start = (page - 1) * pageSize;
        // 将检索的列表数据的结束索引，以列表长度为值，表示获取至最后一个数据
        long end = start + pageSize - 1;
        // 返回检索结果
        return getList(listKey, start, end);
    }

    @Override
    public void setListForCategory(Long categoryId, List<BrandStandardVO> brandList) {
        // 获取列表数据的key
        String listKey = getListByCategoryKey(categoryId);
        // 删除Redis中原列表数据
        log.debug("删除根据类别匹配的品牌列表 >>> key={}", listKey);
        redisTemplate.delete(listKey);

        // 遍历参数数据，反复将各元素数据添加到Redis中的列表的末尾
        for (BrandStandardVO brand : brandList) {
            log.debug("追加品牌列表 >>> key={}, value={}", listKey, brand);
            redisTemplate.opsForList().rightPush(listKey, brand);
        }
    }

    @Override
    public List<BrandStandardVO> listByCategory(Long categoryId) {
        // 获取列表数据的key
        String listKey = getListByCategoryKey(categoryId);
        // 将检索的列表数据的起始索引，0表示从头开始获取数据
        long start = 0;
        // 将检索的列表数据的结束索引，-1表示最末尾
        long end = -1;
        // 获取列表
        return getList(listKey, start, end);
    }

    /**
     * 获取能匹配所有品牌数据项的key的模式
     *
     * @return 能匹配所有品牌数据项的key的模式
     */
    private String getAllKeyPattern() {
        return KEY_PREFIX + "*";
    }

    /**
     * 获取指定id的品牌数据项在Redis中的key
     *
     * @param id 品牌数据项id
     * @return 与id匹配的品牌数据项的key
     */
    private String getItemKey(Long id) {
        return ITEM_KEY_PREFIX + id;
    }

    /**
     * 获取品牌列表数据在Redis中的key
     *
     * @return 品牌列表数据在Redis中的key
     */
    private String getListKey() {
        return LIST_KEY;
    }

    /**
     * 最近重建品牌数据缓存的时间的key
     *
     * @return 最近重建品牌数据缓存的时间的key
     */
    private String getRecentlyLoadTimeKey() {
        return RECENTLY_LOAD_TIME_KEY;
    }

    /**
     * 获取根据类别匹配的品牌列表在Redis中的key
     *
     * @param categoryId 类别id
     * @return 根据类别匹配的品牌列表在Redis中的key
     */
    private String getListByCategoryKey(Long categoryId) {
        return LIST_BY_CATEGORY_KEY_PREFIX + categoryId;
    }

    /**
     * 获取品牌列表数据
     *
     * @param start 从品牌列表中获取数据的起始位置
     * @param end   从品牌列表中获取数据的结束位置
     * @return 品牌列表数据
     */
    private List<BrandStandardVO> getList(String listKey, long start, long end) {
        // 返回检索结果
        List<Serializable> list = redisTemplate.opsForList().range(listKey, start, end);
        log.debug("获取品牌列表 >>> key={}, start={}, end={}, result={}", listKey, start, end, list);
        // 转换结果类型
        List<BrandStandardVO> result = new ArrayList<>();
        if (list != null) {
            for (Serializable item : list) {
                result.add((BrandStandardVO) item);
            }
        }
        return result;
    }

}
